//////////////////////////////////////////ok
#include"stdafx.h"

#include "bochs.h"

void IA32_CPU::INC_ERX(Ia32_Instruction_c *i)
{
	unsigned opcodeReg = i->opcodeReg();
#if defined(IA32_HostAsm_Inc32)
	Bit32u flags32;
	asmInc32(gen_reg[opcodeReg].dword.erx, flags32);
	IA32_setEFlagsOSZAP(flags32);
#else
	Bit32u erx = ++ gen_reg[opcodeReg].dword.erx;
	IA32_SET_FLAGS_OSZAP_RESULT_32(erx, IA32_INSTR_INC32);
#endif
	IA32_CLEAR_64BIT_HIGH(opcodeReg);
}

void IA32_CPU::DEC_ERX(Ia32_Instruction_c *i)
{
	unsigned opcodeReg = i->opcodeReg();
#if defined(IA32_HostAsm_Dec32)
	Bit32u flags32;
	asmDec32(gen_reg[opcodeReg].dword.erx, flags32);
	IA32_setEFlagsOSZAP(flags32);
#else
	Bit32u erx = -- gen_reg[opcodeReg].dword.erx;
	IA32_SET_FLAGS_OSZAP_RESULT_32(erx, IA32_INSTR_DEC32);
#endif
	IA32_CLEAR_64BIT_HIGH(opcodeReg);
}

void IA32_CPU::ADD_EdGd(Ia32_Instruction_c *i)
{
	Bit32u op2_32, op1_32, sum_32;
	op2_32 = IA32_READ_32BIT_REG(i->nnn());
	if (i->modC0()) 
	{
		op1_32 = IA32_READ_32BIT_REG(i->rm());
		sum_32 = op1_32 + op2_32;
		IA32_WRITE_32BIT_REGZ(i->rm(), sum_32);
	}
	else 
	{
		read_RMW_virtual_dword(i->seg(), IA32_RMAddr(i), &op1_32);
		sum_32 = op1_32 + op2_32;
		write_RMW_virtual_dword(sum_32);
	}
	IA32_SET_FLAGS_OSZAPC_32(op1_32, op2_32, sum_32, IA32_INSTR_ADD32);
}

void IA32_CPU::ADD_GdEEd(Ia32_Instruction_c *i)
{
	Bit32u op1_32, op2_32, sum_32;
	unsigned nnn = i->nnn();
	op1_32 = IA32_READ_32BIT_REG(nnn);
	read_virtual_dword(i->seg(), IA32_RMAddr(i), &op2_32);
#if defined(IA32_HostAsm_Add32)
	Bit32u flags32;
	asmAdd32(sum_32, op1_32, op2_32, flags32);
	IA32_setEFlagsOSZAPC(flags32);
#else
	sum_32 = op1_32 + op2_32;
	IA32_SET_FLAGS_OSZAPC_32(op1_32, op2_32, sum_32, IA32_INSTR_ADD32);
#endif
	IA32_WRITE_32BIT_REGZ(nnn, sum_32);
}

void IA32_CPU::ADD_GdEGd(Ia32_Instruction_c *i)
{
	Bit32u op1_32, op2_32, sum_32;
	unsigned nnn = i->nnn();
	op1_32 = IA32_READ_32BIT_REG(nnn);
	op2_32 = IA32_READ_32BIT_REG(i->rm());
#if defined(IA32_HostAsm_Add32)
	Bit32u flags32;
	asmAdd32(sum_32, op1_32, op2_32, flags32);
	IA32_setEFlagsOSZAPC(flags32);
#else
	sum_32 = op1_32 + op2_32;
	IA32_SET_FLAGS_OSZAPC_32(op1_32, op2_32, sum_32, IA32_INSTR_ADD32);
#endif
	IA32_WRITE_32BIT_REGZ(nnn, sum_32);
}

void IA32_CPU::ADD_EAXId(Ia32_Instruction_c *i)
{
	Bit32u op1_32, op2_32, sum_32;
	op1_32 = EAX;
	op2_32 = i->Id();
	sum_32 = op1_32 + op2_32;
	EAX = sum_32;
	IA32_SET_FLAGS_OSZAPC_32(op1_32, op2_32, sum_32, IA32_INSTR_ADD32);
}

void IA32_CPU::ADC_EdGd(Ia32_Instruction_c *i)
{

	bx_bool temp_CF = getB_CF();
	Bit32u op2_32, op1_32, sum_32;
	op2_32 = IA32_READ_32BIT_REG(i->nnn());
	if (i->modC0()) 
	{
		op1_32 = IA32_READ_32BIT_REG(i->rm());
		sum_32 = op1_32 + op2_32 + temp_CF;
		IA32_WRITE_32BIT_REGZ(i->rm(), sum_32);
	}
	else 
	{
		read_RMW_virtual_dword(i->seg(), IA32_RMAddr(i), &op1_32);
		sum_32 = op1_32 + op2_32 + temp_CF;
		write_RMW_virtual_dword(sum_32);
	}
	IA32_SET_FLAGS_OSZAPC_32(op1_32, op2_32, sum_32,(temp_CF) ? IA32_INSTR_ADC32 : IA32_INSTR_ADD32);
}

void IA32_CPU::ADC_GdEd(Ia32_Instruction_c *i)
{
	bx_bool temp_CF = getB_CF();
	Bit32u op1_32, op2_32, sum_32;
	op1_32 = IA32_READ_32BIT_REG(i->nnn());
	if (i->modC0()) 
	{
		op2_32 = IA32_READ_32BIT_REG(i->rm());
	}
	else 
	{
		read_virtual_dword(i->seg(), IA32_RMAddr(i), &op2_32);
	}

	sum_32 = op1_32 + op2_32 + temp_CF;
	IA32_WRITE_32BIT_REGZ(i->nnn(), sum_32);
	IA32_SET_FLAGS_OSZAPC_32(op1_32, op2_32, sum_32,(temp_CF) ? IA32_INSTR_ADC32 : IA32_INSTR_ADD32);
}

void IA32_CPU::ADC_EAXId(Ia32_Instruction_c *i)
{
	bx_bool temp_CF = getB_CF();
	Bit32u op1_32, op2_32, sum_32;
	op1_32 = EAX;
	op2_32 = i->Id();
	sum_32 = op1_32 + op2_32 + temp_CF;
	EAX = sum_32;
	IA32_SET_FLAGS_OSZAPC_32(op1_32, op2_32, sum_32,(temp_CF) ? IA32_INSTR_ADC32 : IA32_INSTR_ADD32);
}

void IA32_CPU::SBB_EdGd(Ia32_Instruction_c *i)
{
	bx_bool temp_CF = getB_CF();
	Bit32u op2_32, op1_32, diff_32;
	op2_32 = IA32_READ_32BIT_REG(i->nnn());
	if (i->modC0()) 
	{
		op1_32 = IA32_READ_32BIT_REG(i->rm());
		diff_32 = op1_32 - (op2_32 + temp_CF);
		IA32_WRITE_32BIT_REGZ(i->rm(), diff_32);
	}
	else 
	{
		read_RMW_virtual_dword(i->seg(), IA32_RMAddr(i), &op1_32);
		diff_32 = op1_32 - (op2_32 + temp_CF);
		write_RMW_virtual_dword(diff_32);
	}
	IA32_SET_FLAGS_OSZAPC_32(op1_32, op2_32, diff_32, (temp_CF) ? IA32_INSTR_SBB32 : IA32_INSTR_SUB32);
}

void IA32_CPU::SBB_GdEd(Ia32_Instruction_c *i)
{
	bx_bool temp_CF = getB_CF();
	Bit32u op1_32, op2_32, diff_32;
	op1_32 = IA32_READ_32BIT_REG(i->nnn());
	if (i->modC0()) 
	{
		op2_32 = IA32_READ_32BIT_REG(i->rm());
	}
	else 
	{
		read_virtual_dword(i->seg(), IA32_RMAddr(i), &op2_32);
	}
	diff_32 = op1_32 - (op2_32 + temp_CF);
	IA32_WRITE_32BIT_REGZ(i->nnn(), diff_32);
	IA32_SET_FLAGS_OSZAPC_32(op1_32, op2_32, diff_32,(temp_CF) ? IA32_INSTR_SBB32 : IA32_INSTR_SUB32);
}

void IA32_CPU::SBB_EAXId(Ia32_Instruction_c *i)
{
	bx_bool temp_CF = getB_CF();
	Bit32u op1_32, op2_32, diff_32;
	op1_32 = EAX;
	op2_32 = i->Id();
	diff_32 = op1_32 - (op2_32 + temp_CF);
	EAX = diff_32;
	IA32_SET_FLAGS_OSZAPC_32(op1_32, op2_32, diff_32,(temp_CF) ? IA32_INSTR_SBB32 : IA32_INSTR_SUB32);
}

void IA32_CPU::SBB_EdId(Ia32_Instruction_c *i)
{
	bx_bool temp_CF = getB_CF();
	Bit32u op2_32, op1_32, diff_32;
	op2_32 = i->Id();
	if (i->modC0()) 
	{
		op1_32 = IA32_READ_32BIT_REG(i->rm());
		diff_32 = op1_32 - (op2_32 + temp_CF);
		IA32_WRITE_32BIT_REGZ(i->rm(), diff_32);
	}
	else 
	{
		read_RMW_virtual_dword(i->seg(), IA32_RMAddr(i), &op1_32);
		diff_32 = op1_32 - (op2_32 + temp_CF);
		write_RMW_virtual_dword(diff_32);
	}
	IA32_SET_FLAGS_OSZAPC_32(op1_32, op2_32, diff_32,(temp_CF) ? IA32_INSTR_SBB32 : IA32_INSTR_SUB32);
}

void IA32_CPU::SUB_EdGd(Ia32_Instruction_c *i)
{

	Bit32u op2_32, op1_32, diff_32;
	op2_32 = IA32_READ_32BIT_REG(i->nnn());
	if (i->modC0()) 
	{
		op1_32 = IA32_READ_32BIT_REG(i->rm());
		diff_32 = op1_32 - op2_32;
		IA32_WRITE_32BIT_REGZ(i->rm(), diff_32);
	}
	else 
	{
		read_RMW_virtual_dword(i->seg(), IA32_RMAddr(i), &op1_32);
		diff_32 = op1_32 - op2_32;
		write_RMW_virtual_dword(diff_32);
	}
	IA32_SET_FLAGS_OSZAPC_32(op1_32, op2_32, diff_32, IA32_INSTR_SUB32);
}

void IA32_CPU::SUB_GdEd(Ia32_Instruction_c *i)
{
	Bit32u op1_32, op2_32, diff_32;
	unsigned nnn = i->nnn();
	op1_32 = IA32_READ_32BIT_REG(nnn);
	if (i->modC0()) 
	{
		op2_32 = IA32_READ_32BIT_REG(i->rm());
	}
	else 
	{
		read_virtual_dword(i->seg(), IA32_RMAddr(i), &op2_32);
	}
#if defined(IA32_HostAsm_Sub32)
	Bit32u flags32;
	asmSub32(diff_32, op1_32, op2_32, flags32);
	IA32_setEFlagsOSZAPC(flags32);
#else
	diff_32 = op1_32 - op2_32;
	IA32_SET_FLAGS_OSZAPC_32(op1_32, op2_32, diff_32, IA32_INSTR_SUB32);
#endif
	IA32_WRITE_32BIT_REGZ(nnn, diff_32);
}

void IA32_CPU::SUB_EAXId(Ia32_Instruction_c *i)
{
	Bit32u op1_32, op2_32, diff_32;
	op1_32 = EAX;
	op2_32 = i->Id();
	diff_32 = op1_32 - op2_32;
	EAX = diff_32;
	IA32_SET_FLAGS_OSZAPC_32(op1_32, op2_32, diff_32, IA32_INSTR_SUB32);
}

void IA32_CPU::CMP_EdGd(Ia32_Instruction_c *i)
{
	Bit32u op2_32, op1_32;
	op2_32 = IA32_READ_32BIT_REG(i->nnn());
	if (i->modC0()) 
	{
		op1_32 = IA32_READ_32BIT_REG(i->rm());
	}
	else 
	{
		read_virtual_dword(i->seg(), IA32_RMAddr(i), &op1_32);
	}
#if defined(IA32_HostAsm_Cmp32)
	Bit32u flags32;
	asmCmp32(op1_32, op2_32, flags32);
	IA32_setEFlagsOSZAPC(flags32);
#else
	Bit32u diff_32 = op1_32 - op2_32;
	IA32_SET_FLAGS_OSZAPC_32(op1_32, op2_32, diff_32, IA32_INSTR_COMPARE32);
#endif
}

void IA32_CPU::CMP_GdEd(Ia32_Instruction_c *i)
{
	Bit32u op1_32, op2_32;
	op1_32 = IA32_READ_32BIT_REG(i->nnn());
	if (i->modC0()) 
	{
		op2_32 = IA32_READ_32BIT_REG(i->rm());
	}
	else 
	{
		read_virtual_dword(i->seg(), IA32_RMAddr(i), &op2_32);
	}
#if defined(IA32_HostAsm_Cmp32)
	Bit32u flags32;
	asmCmp32(op1_32, op2_32, flags32);
	IA32_setEFlagsOSZAPC(flags32);
#else
	Bit32u diff_32 = op1_32 - op2_32;
	IA32_SET_FLAGS_OSZAPC_32(op1_32, op2_32, diff_32, IA32_INSTR_COMPARE32);
#endif
}

void IA32_CPU::CMP_EAXId(Ia32_Instruction_c *i)
{
	Bit32u op1_32, op2_32;
	op1_32 = EAX;
	op2_32 = i->Id();
#if defined(IA32_HostAsm_Cmp32)
	Bit32u flags32;
	asmCmp32(op1_32, op2_32, flags32);
	IA32_setEFlagsOSZAPC(flags32);
#else
	Bit32u diff_32 = op1_32 - op2_32;
	IA32_SET_FLAGS_OSZAPC_32(op1_32, op2_32, diff_32, IA32_INSTR_COMPARE32);
#endif
}

void IA32_CPU::CWDE(Ia32_Instruction_c *i)
{
	/* CBW: no flags are effected */
	EAX = (Bit16s) AX;
}

void IA32_CPU::CDQ(Ia32_Instruction_c *i)
{
	/* CDQ: no flags are affected */
	if (EAX & 0x80000000) 
	{
		EDX = 0xFFFFFFFF;
	}
	else 
	{
		EDX = 0x00000000;
	}
}

void IA32_CPU::CMPXCHG_XBTS(Ia32_Instruction_c *i)
{
	UndefinedOpcode(i);
}

void IA32_CPU::CMPXCHG_IBTS(Ia32_Instruction_c *i)
{
	UndefinedOpcode(i);
}

void IA32_CPU::XADD_EdGd(Ia32_Instruction_c *i)
{
	Bit32u op2_32, op1_32, sum_32;

	/* XADD dst(r/m), src(r)
	* temp <-- src + dst         | sum = op2 + op1
	* src  <-- dst               | op2 = op1
	* dst  <-- tmp               | op1 = sum
	*/

	op2_32 = IA32_READ_32BIT_REG(i->nnn());
	if (i->modC0()) 
	{
		op1_32 = IA32_READ_32BIT_REG(i->rm());
		sum_32 = op1_32 + op2_32;
		IA32_WRITE_32BIT_REGZ(i->nnn(), op1_32);
		IA32_WRITE_32BIT_REGZ(i->rm(), sum_32);
	}
	else 
	{
		read_RMW_virtual_dword(i->seg(), IA32_RMAddr(i), &op1_32);
		sum_32 = op1_32 + op2_32;
		write_RMW_virtual_dword(sum_32);
		IA32_WRITE_32BIT_REGZ(i->nnn(), op1_32);
	}
	IA32_SET_FLAGS_OSZAPC_32(op1_32, op2_32, sum_32, IA32_INSTR_ADD32);
}

void IA32_CPU::ADD_EEdId(Ia32_Instruction_c *i)
{
	Bit32u op2_32, op1_32, sum_32;
	op2_32 = i->Id();
	read_RMW_virtual_dword(i->seg(), IA32_RMAddr(i), &op1_32);
#if defined(IA32_HostAsm_Add32)
	Bit32u flags32;
	asmAdd32(sum_32, op1_32, op2_32, flags32);
	IA32_setEFlagsOSZAPC(flags32);
#else
	sum_32 = op1_32 + op2_32;
	IA32_SET_FLAGS_OSZAPC_32(op1_32, op2_32, sum_32, IA32_INSTR_ADD32);
#endif
	write_RMW_virtual_dword(sum_32);
}

void IA32_CPU::ADD_EGdId(Ia32_Instruction_c *i)
{
	Bit32u op2_32, op1_32, sum_32;
	op2_32 = i->Id();
	op1_32 = IA32_READ_32BIT_REG(i->rm());
#if defined(IA32_HostAsm_Add32)
	Bit32u flags32;
	asmAdd32(sum_32, op1_32, op2_32, flags32);
	IA32_setEFlagsOSZAPC(flags32);
#else
	sum_32 = op1_32 + op2_32;
	IA32_SET_FLAGS_OSZAPC_32(op1_32, op2_32, sum_32, IA32_INSTR_ADD32);
#endif

	IA32_WRITE_32BIT_REGZ(i->rm(), sum_32);
}

void IA32_CPU::ADC_EdId(Ia32_Instruction_c *i)
{
	bx_bool temp_CF = getB_CF();
	Bit32u op2_32, op1_32, sum_32;
	op2_32 = i->Id();
	if (i->modC0()) 
	{
		op1_32 = IA32_READ_32BIT_REG(i->rm());
		sum_32 = op1_32 + op2_32 + temp_CF;
		IA32_WRITE_32BIT_REGZ(i->rm(), sum_32);
	}
	else 
	{
		read_RMW_virtual_dword(i->seg(), IA32_RMAddr(i), &op1_32);
		sum_32 = op1_32 + op2_32 + temp_CF;
		write_RMW_virtual_dword(sum_32);
	}
	IA32_SET_FLAGS_OSZAPC_32(op1_32, op2_32, sum_32, (temp_CF) ? IA32_INSTR_ADC32 : IA32_INSTR_ADD32);
}

void IA32_CPU::SUB_EdId(Ia32_Instruction_c *i)
{
	Bit32u op2_32, op1_32, diff_32;
	op2_32 = i->Id();
	if (i->modC0()) 
	{
		op1_32 = IA32_READ_32BIT_REG(i->rm());
		diff_32 = op1_32 - op2_32;
		IA32_WRITE_32BIT_REGZ(i->rm(), diff_32);
	}
	else 
	{
		read_RMW_virtual_dword(i->seg(), IA32_RMAddr(i), &op1_32);
		diff_32 = op1_32 - op2_32;
		write_RMW_virtual_dword(diff_32);
	}
	IA32_SET_FLAGS_OSZAPC_32(op1_32, op2_32, diff_32, IA32_INSTR_SUB32);
}

void IA32_CPU::CMP_EdId(Ia32_Instruction_c *i)
{
	Bit32u op2_32, op1_32;
	op2_32 = i->Id();
	if (i->modC0()) 
	{
		op1_32 = IA32_READ_32BIT_REG(i->rm());
	}
	else 
	{
		read_virtual_dword(i->seg(), IA32_RMAddr(i), &op1_32);
	}
#if defined(IA32_HostAsm_Cmp32)
	Bit32u flags32;
	asmCmp32(op1_32, op2_32, flags32);
	IA32_setEFlagsOSZAPC(flags32);
#else
	Bit32u diff_32;
	diff_32 = op1_32 - op2_32;
	IA32_SET_FLAGS_OSZAPC_32(op1_32, op2_32, diff_32, IA32_INSTR_COMPARE32);
#endif
}

void IA32_CPU::NEG_Ed(Ia32_Instruction_c *i)
{
	Bit32u op1_32, diff_32;
	if (i->modC0()) 
	{
		op1_32 = IA32_READ_32BIT_REG(i->rm());
		diff_32 = 0 - op1_32;
		IA32_WRITE_32BIT_REGZ(i->rm(), diff_32);
	}
	else 
	{
		read_RMW_virtual_dword(i->seg(), IA32_RMAddr(i), &op1_32);
		diff_32 = 0 - op1_32;
		write_RMW_virtual_dword(diff_32);
	}
	IA32_SET_FLAGS_OSZAPC_RESULT_32(diff_32, IA32_INSTR_NEG32);
}

void IA32_CPU::INC_Ed(Ia32_Instruction_c *i)
{
	Bit32u op1_32;
	if (i->modC0()) 
	{
		op1_32 = IA32_READ_32BIT_REG(i->rm());
		op1_32++;
		IA32_WRITE_32BIT_REGZ(i->rm(), op1_32);
	}
	else 
	{
		read_RMW_virtual_dword(i->seg(), IA32_RMAddr(i), &op1_32);
		op1_32++;
		write_RMW_virtual_dword(op1_32);
	}
	IA32_SET_FLAGS_OSZAP_RESULT_32(op1_32, IA32_INSTR_INC32);
}

void IA32_CPU::DEC_Ed(Ia32_Instruction_c *i)
{
	Bit32u op1_32;
	if (i->modC0()) 
	{
		op1_32 = IA32_READ_32BIT_REG(i->rm());
		op1_32--;
		IA32_WRITE_32BIT_REGZ(i->rm(), op1_32);
	}
	else 
	{
		read_RMW_virtual_dword(i->seg(), IA32_RMAddr(i), &op1_32);
		op1_32--;
		write_RMW_virtual_dword(op1_32);
	}
	IA32_SET_FLAGS_OSZAP_RESULT_32(op1_32, IA32_INSTR_DEC32);
}

void IA32_CPU::CMPXCHG_EdGd(Ia32_Instruction_c *i)
{
	Bit32u op2_32, op1_32, diff_32;
	if (i->modC0()) 
	{
		op1_32 = IA32_READ_32BIT_REG(i->rm());
	}
	else 
	{
		read_RMW_virtual_dword(i->seg(), IA32_RMAddr(i), &op1_32);
	}

	diff_32 = EAX - op1_32;
	IA32_SET_FLAGS_OSZAPC_32(EAX, op1_32, diff_32, IA32_INSTR_COMPARE32);
	if (diff_32 == 0) 
	{  // if accumulator == dest
		// dest <-- src
		op2_32 = IA32_READ_32BIT_REG(i->nnn());
		if (i->modC0()) 
		{
			IA32_WRITE_32BIT_REGZ(i->rm(), op2_32);
		}
		else 
		{
			write_RMW_virtual_dword(op2_32);
		}
	}
	else 
	{
		// accumulator <-- dest
		EAX = op1_32;
	}

}

void IA32_CPU::CMPXCHG8B(Ia32_Instruction_c *i)
{
	Bit32u op1_64_lo, op1_64_hi, diff;

	if (i->modC0()) 
	{
		UndefinedOpcode(i);
	}

	read_virtual_dword(i->seg(), IA32_RMAddr(i), &op1_64_lo);
	read_RMW_virtual_dword(i->seg(), IA32_RMAddr(i) + 4, &op1_64_hi);
	diff  = EAX - op1_64_lo;
	diff |= EDX - op1_64_hi;
	if (diff == 0) 
	{  // if accumulator == dest
		// ZF = 1
		set_ZF(1);
		// dest <-- src
		write_RMW_virtual_dword(ECX);
		write_virtual_dword(i->seg(), IA32_RMAddr(i), &EBX);
	}
	else 
	{
		// ZF = 0
		set_ZF(0);
		// accumulator <-- dest
		EAX = op1_64_lo;
		EDX = op1_64_hi;
	}
}
